package com.drcoffee.drcoffeemall.service.impl;

import com.drcoffee.drcoffeemall.entry.Order;
import com.drcoffee.drcoffeemall.entry.Result;
import com.drcoffee.drcoffeemall.entry.Trade;
import com.drcoffee.drcoffeemall.mapper.OrderMapper;
import com.drcoffee.drcoffeemall.mapper.TradeMapper;
import com.drcoffee.drcoffeemall.service.WeChatPayService;
import com.drcoffee.drcoffeemall.util.AllPayVerifyUtil;
import com.drcoffee.drcoffeemall.util.SendWxWorlUtil;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.exception.ValidationException;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.partnerpayments.nativepay.model.Transaction;
import com.wechat.pay.java.service.payments.nativepay.NativePayService;
import com.wechat.pay.java.service.payments.nativepay.model.*;
import com.wechat.pay.java.service.refund.model.RefundNotification;
import com.wechat.pay.java.service.refund.model.Status;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import java.io.BufferedReader;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Service
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public class WeChatPayServiceImpl implements WeChatPayService {
    @Autowired
    TradeMapper tradeMapper;

    @Autowired
    OrderMapper orderMapper;

    @Autowired
    AllPayVerifyUtil allPayVerifyUtil;

    /**
     * app_id
     */
    @Value("${wechat.app_id}")
    private String app_id;

    /**
     * 商户号
     */
    @Value("${wechat.merchantId}")
    private String merchantId = "1489444742";

    /**
     * 商户API私钥路径
     */
    @Value("${wechat.privateKeyPath}")
    private String privateKeyPath;

    /**
     * 商户证书序列号
     */
    @Value("${wechat.merchantSerialNumber}")
    private String merchantSerialNumber;

    /**
     * 商户APIV3密钥
     */
    @Value("${wechat.apiV3Key}")
    private String apiV3Key;

    /**
     * 服务器异步通知页面路径
     */
    @Value("${wechat.notify_url}")
    private String notify_url;

    /*
     * 创建支付单
     * */
    @Override
    public Result createPay(String order_code) {
        try {
            log.info("=============== 进入createPay");
            Integer tradeNum = tradeMapper.getTradeNum(order_code, "I");
            if (tradeNum == 0) {
                Config config =
                        new RSAAutoCertificateConfig.Builder()
                                .merchantId(merchantId)
                                .privateKeyFromPath(privateKeyPath)
                                .merchantSerialNumber(merchantSerialNumber)
                                .apiV3Key(apiV3Key)
                                .build();

                // 根据订单号查询订单价格并转换单位为分
                Order order = orderMapper.getOrderByCode(order_code);
                BigDecimal orderTotal = order.getOrder_total();
                Integer newOrderTotal = orderTotal.multiply(BigDecimal.valueOf(100)).intValue();


                NativePayService service = new NativePayService.Builder().config(config).build();// 构建service

                PrepayRequest request = new PrepayRequest();
                Amount amount = new Amount();
                amount.setTotal(newOrderTotal);// 订单总金额(单位：分)
                request.setAmount(amount);// 金额(单位：分)
                request.setAppid(app_id);
                request.setMchid(merchantId);
                request.setDescription("Dr.coffee 配件订单：" + order_code);
                request.setNotifyUrl(notify_url);
                request.setOutTradeNo(order_code);
                PrepayResponse response = service.prepay(request);
                // 返回支付链接（由前端转为二维码）
                return new Result(1, "SUCCESS", "", response.getCodeUrl());
            } else {
                return new Result(2, "SUCCESS", "", null);
            }
        } catch (Exception e) {
            log.error("Exception", e);
            return new Result(-1, "ERROR", e.getMessage(), null);
        }
    }


    /*
     * 接收支付通知
     * */
    @Override
    public Map<String, String> wechatNotify(HttpServletRequest request) {
        Map<String, String> resultMap = new HashMap<>();
        try {
            log.info("=============== 进入wechatNotify");
            RequestParam requestParam = getRequestParam(request);
            NotificationConfig config = getNotificationConfig();

            // 初始化 NotificationParser
            NotificationParser parser = new NotificationParser(config);
            // 以支付通知回调为例，验签、解密并转换成 Transaction
            Transaction transaction = parser.parse(requestParam, Transaction.class);
            Integer aliResultCode = allPayVerifyUtil.aliPayVerify(transaction.getOutTradeNo());
            if (aliResultCode != -2) {
                allPayVerifyUtil.closeAliPay(transaction.getOutTradeNo());
            }

            if (transaction.getTradeState() == Transaction.TradeStateEnum.SUCCESS) {
                // 判断交易是否存在("防止重复调用")
                Integer tradeNum = tradeMapper.getTradeNum(transaction.getOutTradeNo(), "I");
                if (tradeNum == 0) {
                    // 支付成功，更改订单状态
                    orderMapper.setStatus(transaction.getOutTradeNo(), "2", "status_time_02", "");
                    Integer payerTotal = transaction.getAmount().getPayerTotal();// 支付金额(单位分)
                    BigDecimal total = new BigDecimal(String.valueOf(payerTotal)).divide(BigDecimal.valueOf(100));// 单位转换为 元

                    // 存储交易记录
                    Trade trade = new Trade(null, transaction.getOutTradeNo(), transaction.getSuccessTime(), transaction.getTransactionId(), "", total, "I", 1);
                    tradeMapper.saveTrade(trade);
                    orderMapper.setPayMethod(transaction.getOutTradeNo(), 1);
                }
                // 处理成功，返回 200 OK 状态码
                resultMap.put("code", "200");
            } else {
                log.error("=============== 支付失败：订单号" + transaction.getOutTradeNo());
                // 如果处理失败，应返回 4xx/5xx 的状态码，例如 500 INTERNAL_SERVER_ERROR
                log.error(transaction.getTradeStateDesc());
                resultMap.put("code", "500");
            }
            resultMap.put("message", "");
            return resultMap;
        } catch (ValidationException e) {
            log.error("=============== 签名验证失败，返回 401 状态码");
            log.error("ValidationException", e);
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            resultMap.put("code", "401");
            resultMap.put("message", "");
            return resultMap;
        }
    }

    /* 查询订单 */
    public Result payVerify(String order_code) {
        Integer resultCode = allPayVerifyUtil.wxPayVerify(order_code);
        if (resultCode == 1) {
            return new Result(1, "", "", null);
        } else {
            return new Result(2, "", "", null);
        }
    }


    /*
     * 接收退款通知
     * */
    @Override
    public Map<String, String> notifyRefund(HttpServletRequest request) {
        Map<String, String> resultMap = new HashMap<>();
        try {
            log.info("=============== 进入notifyRefund");
            // 构造 RequestParam
            RequestParam requestParam = getRequestParam(request);
            NotificationConfig config = getNotificationConfig();

            // 初始化 NotificationParser
            NotificationParser parser = new NotificationParser(config);
            // 以支付通知回调为例，验签、解密并转换成 RefundNotification
            RefundNotification refundNotification = parser.parse(requestParam, RefundNotification.class);

            if (refundNotification.getRefundStatus() == Status.SUCCESS) {
                //退款成功，存储交易记录
                // 判断交易是否存在("防止重复调用")
                Integer tradeNum = tradeMapper.getTradeNum(refundNotification.getOutTradeNo(), "O");
                if (tradeNum == 0) {
                    Long payerTotal = refundNotification.getAmount().getPayerTotal();// 交易金额(单位分)
                    BigDecimal total = new BigDecimal(String.valueOf(payerTotal)).divide(BigDecimal.valueOf(100));// 单位转换为元
                    Trade trade = new Trade(null, refundNotification.getOutTradeNo(), refundNotification.getSuccessTime(), refundNotification.getTransactionId(), refundNotification.getRefundId(), total, "O", 1);
                    tradeMapper.saveTrade(trade);
                    orderMapper.setPayMethod( refundNotification.getOutTradeNo(), 1);
                }
                resultMap.put("code", "200");
            } else {
                log.error("=============== 退款失败：售后单号（退款单号）" + refundNotification.getRefundId());
                resultMap.put("code", "500");
            }
            resultMap.put("message", "");
            return resultMap;
        } catch (ValidationException e) {
            log.error("=============== 签名验证失败，返回 401 状态码");
            log.error("ValidationException", e);
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            resultMap.put("code", "401");
            resultMap.put("message", "");
            return resultMap;
        }
    }


    // 构造 RequestParam
    public RequestParam getRequestParam(HttpServletRequest request) {
        return new RequestParam.Builder()
                .serialNumber(request.getHeader("Wechatpay-Serial"))
                .nonce(request.getHeader("Wechatpay-Nonce"))
                .signature(request.getHeader("Wechatpay-Signature"))
                .timestamp(request.getHeader("Wechatpay-Timestamp"))
                .body(ReadAsChars(request))
                .build();
    }

    public NotificationConfig getNotificationConfig() {
        return new RSAAutoCertificateConfig.Builder()
                .merchantId(merchantId)
                .privateKeyFromPath(privateKeyPath)
                .merchantSerialNumber(merchantSerialNumber)
                .apiV3Key(apiV3Key)
                .build();
    }


    /**
     * 获取request中的请求body
     */
    public static String ReadAsChars(HttpServletRequest request) {
        BufferedReader br = null;
        StringBuilder sb = new StringBuilder();
        try {
            br = request.getReader();
            String str;
            while ((str = br.readLine()) != null) {
                sb.append(str);
            }
            br.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != br) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return sb.toString();
    }
}
